package cn.git.agent.util;

import cn.git.agent.basic.BasicMethodBuildSrc;
import javassist.*;
import lombok.Data;

import java.io.IOException;

/**
 * @description: 探针重构方法工具类
 * @program: bank-credit-sy
 * @author: lixuchun
 * @create: 2024-03-19 08:50:17
 */
@Data
public class AgentSrcBuilderUtil {

    /**
     * 类名称
     */
    private String className;

    /**
     * 类加载器
     */
    private ClassLoader loader;

    /**
     * 重构类
     */
    private CtClass ctclass;

    /**
     * 构造函数
     *
     * @param className 类名称
     * @param loader    类加载器
     * @param ctclass   重构类
     */
    public AgentSrcBuilderUtil(String className, ClassLoader loader, CtClass ctclass) {
        this.className = className;
        this.loader = loader;
        this.ctclass = ctclass;
    }

    /**
     * 正常插入source信息
     * 参数写法
     *     %s 表示 原String format替换标识符
     *     $$ 表示 arg1 arg2 arg3 ...
     *     $1 表示 arg1
     *     $2 表示 arg2
     *     $args  表示  Object[]
     *     $w 表示自动返回原来的类型
     */
    public static final String SOURCE = "{\n"
            + "%s"
            + "        Object result=null;\n"
            + "       try {\n"
            + "            result=($w)%s$agent($$);\n"
            + "        } catch (Throwable e) {\n"
            + "%s"
            + "            throw e;\n"
            + "        }finally{\n"
            + "%s"
            + "        }\n"
            + "        return ($r) result;\n"
            + "}\n";

    /**
     * 无返回值的source信息
     */
    public static final String VOID_SOURCE = "{\n"
            + "%s"
            + "        try {\n"
            + "            %s$agent($$);\n"
            + "        } catch (Throwable e) {\n"
            + "%s"
            + "            throw e;\n"
            + "        }finally{\n"
            + "%s"
            + "        }\n"
            + "}\n";


    /**
     * 生成字节码方法
     *
     * @param method 修改方法
     * @param basicMethodBuildSrc 插入信息
     * @throws CannotCompileException
     */
    public void updateTargetMethod(CtMethod method, BasicMethodBuildSrc basicMethodBuildSrc)
            throws CannotCompileException {
        // 获取方法名称
        String methodName = method.getName();
        // 重构被代理的方法名称,新增代理方法
        CtMethod agentMethod = CtNewMethod.copy(method, methodName, ctclass, null);
        agentMethod.setName(methodName + "$agent");
        ctclass.addMethod(agentMethod);
        // 原方法重置为代理执行
        method.setBody(buildBasicMethodSrc(method, basicMethodBuildSrc));
    }

    /**
     * 生成字节码
     *
     * @return
     * @throws IOException,CannotCompileException
     */
    public byte[] toNewBytecode() throws IOException, CannotCompileException {
        return ctclass.toBytecode();
    }

    public String buildBasicMethodSrc(CtMethod method, BasicMethodBuildSrc basicMethodBuildSrc) {
        try {
            // 选择模板信息，通过是否有返回值进行判断
            String template = method.getReturnType().getName().equals("void") ? VOID_SOURCE : SOURCE;
            // 开始插入信息
            String finalBeginSrc = basicMethodBuildSrc.getBeginInsertSrc() == null ? ""
                    : basicMethodBuildSrc.getBeginInsertSrc();
            // 异常插入信息
            String finalErrorSrc = basicMethodBuildSrc.getErrorInsertSrc() == null ? ""
                    : basicMethodBuildSrc.getErrorInsertSrc();

            // 结束插入信息
            String finalEndSrc = basicMethodBuildSrc.getEndInsertSrc() == null ? ""
                    : basicMethodBuildSrc.getEndInsertSrc();
            return String.format(template, finalBeginSrc, method.getName(), finalErrorSrc, finalEndSrc);
        } catch (NotFoundException e) {
            throw new RuntimeException(e);
        }
    }

}
